﻿#include "gameboard.h"
#include <QRandomGenerator>
#include <qpainter.h>
#include <qdebug.h>
#include <queue>

GameBoard::GameBoard(QWidget* parent)
    : QWidget(parent)
{
    // Initialize the game board with null pointers
    for (int i = 0; i < 10; i++) {
        QVector<Tile*> row;
        for (int j = 0; j < 10; j++) {
            row.push_back(nullptr);
        }
        board.push_back(row);
    }

    // Create a layout for the game board
    gameLayout = new QGridLayout;
    gameLayout->setSpacing(0);
    gameLayout->setContentsMargins(0, 0, 0, 0);
    gameLayout->setGeometry(QRect(0, 0, 500, 500)); // Set the layout size to 500x500 pixels
    for (int i = 0; i < 10; i++) {
        gameLayout->setColumnMinimumWidth(i, 50); // Set each column's minimum width to 50 pixels
        gameLayout->setRowMinimumHeight(i, 50); // Set each row's minimum height to 50 pixels
    }
    setLayout(gameLayout);

    // Connect the startNewGame slot to the gameWon signal
    connect(this, &GameBoard::gameWon, this, &GameBoard::startNewGame);
}

GameBoard::~GameBoard()
{
    clearBoard();
}

void GameBoard::startNewGame()
{
    clearBoard();
    createNewBoard();

}

void GameBoard::clearBoard()
{
    for (int i = 0; i < 10; i++) {
        for (int j = 0; j < 10; j++) {
            if (board[i][j] != nullptr) {
                delete board[i][j];
                board[i][j] = nullptr;
            }
        }
    }
}

void GameBoard::createNewBoard()
{
    QVector<int> types;
    for (int i = 0; i < 50; i++) {
        int type = QRandomGenerator::global()->bounded(1, 5);
        types.push_back(type);
        types.push_back(type);
    }
    std::random_shuffle(types.begin(), types.end());
    for (int i = 0; i < 10; i++) {
        for (int j = 0; j < 10; j++) {
            int type = types[i * 10 + j];
            Tile* tile = new Tile(type,i,j);
            connect(tile, &Tile::clicked, this, &GameBoard::onTileClicked);
            board[i][j] = tile;
            gameLayout->addWidget(tile, i, j);
        }
    }
}

void GameBoard::checkGameWon()
{
    // Check if all tiles have been removed
    for (int i = 0; i < 10; i++) {
        for (int j = 0; j < 10; j++) {
            if (board[i][j] != nullptr) {
                return;
            }
        }
    }

    // If all tiles have been removed, emit the gameWon signal
    emit gameWon();
}

void GameBoard::onTileClicked(Tile* tile)
{
    if (firstClickedTile == nullptr) {
        firstClickedTile = tile; // If this is the first clicked tile, store it
         // Change the background color of the tile to highlight it
        QPalette palette = tile->palette();
        palette.setColor(QPalette::Background, Qt::yellow);
        tile->setPalette(palette);
        tile->setStyleSheet("border: 3px solid yellow;");

        // You may also need to call this function to ensure the widget is repainted after changing the palette
        tile->update();
    }
    else {
        firstClickedTile->setStyleSheet("border: 0px solid yellow;");
        if (firstClickedTile == tile) {
            // If the same tile is clicked twice
            firstClickedTile = nullptr;
            return;
        }
        if (firstClickedTile->getType() == tile->getType() && 
            isPathExist(firstClickedTile, tile)) {
            // Traverse the board and set the tiles to be deleted to nullptr, then delete them
            int firstTileX = firstClickedTile->getX();
            int firstTileY = firstClickedTile->getY();
            int secondTileX = tile->getX();
            int secondTileY = tile->getY();
            board[firstTileX][firstTileY] = nullptr;
            board[secondTileX][secondTileY] = nullptr;
            // If the two clicked tiles are the same, delete them
            delete firstClickedTile;
            delete tile;
            // Check if the game is won
            checkGameWon();
        }
        firstClickedTile = nullptr; // Reset the first clicked tile
    }
}

bool GameBoard::isPathExist(Tile* tile1, Tile* tile2) {
    // Initialize dp array,其中 int 代表当前转向的次数, dp记录上一次的转向方向
    const int TurnNumber = 2;
    QVector<QVector<QVector<int>>> dp(10, QVector<QVector<int>>(10, QVector<int>(4, INT_MAX)));
    dp[tile1->getX()][tile1->getY()][0] = 0;
    dp[tile1->getX()][tile1->getY()][1] = 0;
    dp[tile1->getX()][tile1->getY()][2] = 0;
    dp[tile1->getX()][tile1->getY()][3] = 0;

    // Initialize path array to store the optimal path
    QVector<QVector<QVector<QPair<int, int>>>> path(10, QVector<QVector<QPair<int, int>>>(10, QVector<QPair<int, int>>(4)));

    // BFS function
    std::queue<std::tuple<int, int, int>> q;
    q.push(std::make_tuple(tile1->getX(), tile1->getY(), 0));
    q.push(std::make_tuple(tile1->getX(), tile1->getY(), 1));
    q.push(std::make_tuple(tile1->getX(), tile1->getY(), 2));
    q.push(std::make_tuple(tile1->getX(), tile1->getY(), 3));
    while (!q.empty()) {
        std::tuple<int, int, int> front = q.front();
        int x = std::get<0>(front);
        int y = std::get<1>(front);
        int dir = std::get<2>(front);
        q.pop();
        std::vector<std::pair<int, int>> directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
        for (int i = 0; i < 4; i++) {
            int dx = directions[i].first;
            int dy = directions[i].second;
            int nx = x + dx, ny = y + dy, turns = dp[x][y][dir] + (i != dir);
            if (nx < 0 || nx >= 10 || ny < 0 || ny >= 10 || turns > TurnNumber) {
                continue;
            }
            if (board[nx][ny] && !(nx == tile1->getX() && ny == tile1->getY())
                && !(nx == tile2->getX() && ny == tile2->getY())) {
                continue;
            }
            if (turns < dp[nx][ny][i]) {
                dp[nx][ny][i] = turns;
                path[nx][ny][i] = {x, y}; // Store the previous point in the path
                q.push(std::make_tuple(nx, ny, i));
            }
        }
    }
    bool found= std::min({ dp[tile2->getX()][tile2->getY()][0],
        dp[tile2->getX()][tile2->getY()][1],
        dp[tile2->getX()][tile2->getY()][2],
        dp[tile2->getX()][tile2->getY()][3] }) <= TurnNumber;
    if (found)
    {
        drawPath.clear();
        // Store the points to be drawn in the drawPath variable
        int x = tile2->getX();
        int y = tile2->getY();
        std::vector<int> dirs = { dp[x][y][0], dp[x][y][1], dp[x][y][2], dp[x][y][3] };
        int dir = std::min_element(dirs.begin(), dirs.end()) - dirs.begin();
        // head
        drawPath.push_back(QPair<int, int>(x * 50 + 25, y * 50 + 25));
        while (x != tile1->getX() || y != tile1->getY()) {
            int px = path[x][y][dir].first;
            int py = path[x][y][dir].second;
            std::vector<int> dirs = { dp[px][py][0], dp[px][py][1], dp[px][py][2], dp[px][py][3] };
            dir = std::min_element(dirs.begin(), dirs.end()) - dirs.begin();
            // Store the previous point and the current point in the drawPath
            drawPath.push_back(QPair<int, int>(x * 50 + 25, y * 50 + 25));
            x = px;
            y = py;
        }
        // trial
        drawPath.push_back(QPair<int, int>(x * 50 + 25, y * 50 + 25));
        update();
        return true;
    }
    // Return the minimum value among all directions
    return false;
}



void GameBoard::paintEvent(QPaintEvent* event)
{
    QPainter painter(this);

    // 设置画笔颜色为蓝色
    painter.setPen(QPen(Qt::blue));

    // 设置画笔宽度
    painter.setPen(QPen(Qt::blue, 2, Qt::SolidLine));
    // 获取drawPath中的路径并绘制线条
    for (int i = 0; i < drawPath.size() - 1; i++) {
        QPair<int, int> start = drawPath[i];
        QPair<int, int> end = drawPath[i + 1];
        painter.drawLine(start.second,start.first , end.second,end.first);
    }
}